package snake

import (
	"container/list"
	"fmt"
	"github.com/mattn/go-runewidth"
	"github.com/nsf/termbox-go"
	"os"
	"snake2/util"
	"time"
)

func newSnake() *engine {
	e := &engine{
		w:          width,
		h:          height,
		marginLeft: left,
		marginTop:  top,
		speed:      time.Millisecond * 1000,
		list:       list.New(),
	}
	e.c = time.NewTicker(e.speed - time.Duration(e.hard)*time.Millisecond*10)
	maps := [width]Cell{}
	for i := 0; i < e.h; i++ {
		e.globalMap[i] = maps
	}
	return e
}

func Start() {
	if err := termbox.Init(); err != nil {
		fmt.Println(err)
		return
	}
	defer termbox.Close()
	snake = newSnake()
	go snake.listenKeyboard(keyboardEventsChan)
	snake.run()
}

func (e *engine) run() {
RESTART:
	e.createFood()
	e.createSnake()
	e.flush()
	for {
		select {
		case key := <-keyboardEventsChan:
			if e.gameOver && key.eventType != "restart" {
				break
			}
			if key.eventType != "close" && key.eventType != "space" && key.eventType != "restart" && e.state == 1 {
				break
			}
			switch key.eventType {
			case "close":
				fmt.Printf("\r\n")
				os.Exit(1)
			case "space":
				e.state = 1
			case "left":
				e.left()
			case "right":
				e.right()
			case "up": //变形
				e.up()
			case "down":
				e.down()
			case "restart":
				snake = newSnake()
				goto RESTART
			}
		case <-e.c.C:
			if e.state == 0 && e.direction != "" {
				e.move()
			}
		default:
			e.flush() //刷新界面
		}
	}
}

// 刷新键盘
func (e *engine) listenKeyboard(keyboardEventsChan chan<- keyboardEvent) {
	for {
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			switch ev.Key {
			case termbox.KeyArrowUp: //小键盘向上
				keyboardEventsChan <- keyboardEvent{eventType: "up"}
			case termbox.KeyArrowDown: //小键盘向下
				keyboardEventsChan <- keyboardEvent{eventType: "down"}
			case termbox.KeyArrowLeft: //小键盘向左
				keyboardEventsChan <- keyboardEvent{eventType: "left"}
			case termbox.KeyArrowRight: //小键盘向右
				keyboardEventsChan <- keyboardEvent{eventType: "right"}
			case termbox.KeyF5: //F5 用来重置地图
				keyboardEventsChan <- keyboardEvent{eventType: "restart"}
			case termbox.KeyEsc: //end结束游戏
				keyboardEventsChan <- keyboardEvent{eventType: "close"}
			case termbox.KeySpace: //开始或者暂停游戏
				keyboardEventsChan <- keyboardEvent{eventType: "space"}
			}
		}
	}
}

// 刷新地图
func (e *engine) flush() {
	termbox.Clear(termbox.ColorDarkGray, termbox.ColorBlue)
	left := e.w + e.marginLeft + 2
	top := e.marginTop + 1
	e.renderArena() //刷新场地
	e.renderFood()
	e.renderSnake()
	e.renderSome(left, &top, fmt.Sprintf("得分:%d", 10000000))
	e.renderSome(left, &top, fmt.Sprintf("难度等级:%d", e.hard))
	e.renderSome(left, &top, fmt.Sprintf("%s:%s", "开始/暂停", "空格"))
	e.renderSome(left, &top, fmt.Sprintf("%s:%s", "向上", "up"))
	e.renderSome(left, &top, fmt.Sprintf("%s:%s", "向下", "down"))
	e.renderSome(left, &top, fmt.Sprintf("%s:%s", "向左", "left"))
	e.renderSome(left, &top, fmt.Sprintf("%s:%s", "向右", "right"))
	if e.gameOver {
		e.renderSome(left, &top, fmt.Sprintf("%s:%s", "游戏结束", "end!"))
	}
	termbox.Flush()
}

// 游戏场地
func (e *engine) renderArena() {
	for y := 0; y < e.h; y++ {
		for x := 0; x < e.w; x++ {
			termbox.SetCell(x+e.marginLeft, y+e.marginTop, ' ', fgColor, bgColor)
		}
	}
}
func (e *engine) renderFood() {
	for _, v := range e.food {
		termbox.SetCell(v.x, v.y, ' ', v.Fg, v.Bg)
	}
}
func (e *engine) renderSnake() {
	for l := e.list.Front(); l != nil; l = l.Next() {
		if data, ok := l.Value.([]Cell); ok {
			for _, v := range data {
				termbox.SetCell(v.x, v.y, ' ', v.Fg, v.Bg)
			}
		}
	}
}

func (e *engine) renderSome(left int, top *int, text string) {
	for _, v := range text {
		termbox.SetCell(left, *top, rune(v), fgColor, bgColor)
		left += runewidth.RuneWidth(v)
	}
	*top += 2
}

func (e *engine) left() {
	font := e.list.Front().Value.([]Cell)
	next := e.list.Front().Next()
	if next != nil {
		if next, ok := e.list.Front().Next().Value.([]Cell); ok {
			if font[0].y == next[0].y && font[0].x > next[0].x {
				return
			}
		}
	}
	e.direction = "left"
}
func (e *engine) right() {
	font := e.list.Front().Value.([]Cell)
	next := e.list.Front().Next()
	if next != nil {
		if next, ok := e.list.Front().Next().Value.([]Cell); ok {
			if font[0].y == next[0].y && font[0].x < next[0].x {
				return
			}
		}
	}

	e.direction = "right"
}
func (e *engine) up() {
	font := e.list.Front().Value.([]Cell)
	next := e.list.Front().Next()
	if next != nil {
		if next, ok := e.list.Front().Next().Value.([]Cell); ok {
			if font[0].x == next[0].x && font[0].y > next[0].x {
				return
			}
		}
	}
	e.direction = "up"
}
func (e *engine) down() {
	font := e.list.Front().Value.([]Cell)
	next := e.list.Front().Next()
	if next != nil {
		if next, ok := e.list.Front().Next().Value.([]Cell); ok {
			if font[0].x == next[0].x && font[0].y < next[0].x {
				return
			}
		}
	}
	e.direction = "down"
}

func (e *engine) move() {
	switch e.direction {
	case "left":
		cell, ok := e.list.Front().Value.([]Cell)
		if ok {
			if cell[0].x-1 <= e.marginLeft {
				e.gameOver = true
				return
			}
		}
		getCell := termbox.GetCell(cell[0].x-1, cell[0].y)
		if getCell.Fg == snakeFgColor {
			e.gameOver = true
			return
		}
		if getCell.Fg == foodFgColor {
			e.eat(e.food)
		}
		e.list.Remove(e.list.Front())
		e.list.PushBack([]Cell{
			{
				x:  cell[0].x - 1,
				y:  cell[0].y,
				Fg: cell[0].Fg,
				Bg: cell[0].Bg,
			},
			{
				x:  cell[0].x - 2,
				y:  cell[0].y,
				Fg: cell[0].Fg,
				Bg: cell[0].Bg,
			},
		})
	case "right":
		cell, ok := e.list.Front().Value.([]Cell)
		if ok {
			if cell[0].x+1 >= e.w+e.marginLeft {
				e.gameOver = true
				return
			}
		}
		getCell := termbox.GetCell(cell[0].x+1, cell[0].y)
		if getCell.Fg == snakeFgColor {
			e.gameOver = true
			return
		}
		if getCell.Fg == foodFgColor {
			e.eat(e.food)
		}
	case "up":
		cell, ok := e.list.Front().Value.([]Cell)
		if ok {
			if cell[0].y-1 <= e.marginTop {
				e.gameOver = true
				return
			}
		}
		getCell := termbox.GetCell(cell[0].x, cell[0].y-1)
		if getCell.Fg == snakeFgColor {
			e.gameOver = true
			return
		}
		if getCell.Fg == foodFgColor {
			e.eat(e.food)
		}
	case "down":
		cell, ok := e.list.Front().Value.([]Cell)
		if ok {
			if cell[0].y+1 >= e.h+e.marginLeft {
				e.gameOver = true
				return
			}
		}
		getCell := termbox.GetCell(cell[0].x, cell[0].y+1)
		if getCell.Fg == snakeFgColor {
			e.gameOver = true
			return
		}
		if getCell.Fg == foodFgColor {
			e.eat(e.food)
		}
	}
}

func (e *engine) eat(food []Cell) {
	e.list.PushFront(food)
}

func (e *engine) createFood() {
	e.food = []Cell{}
	x := util.Random(e.w/2 - 1)
	y := util.Random(e.h - 1)
	e.food = []Cell{
		{
			x:  x + e.marginLeft,
			y:  y + e.marginTop,
			Fg: foodFgColor,
			Bg: foodBgColor,
		},
		{
			x:  x + e.marginLeft + 1,
			y:  y + e.marginTop,
			Fg: foodFgColor,
			Bg: foodBgColor,
		},
	}
}

// 创建蛇
func (e *engine) createSnake() {
	x := e.w/2 + e.marginLeft
	y := e.h/2 + e.marginTop
	snake := []Cell{
		{
			x:  x,
			y:  y,
			Fg: snakeFgColor,
			Bg: snakeBgColor,
		},
		{
			x:  x + 1,
			y:  y,
			Fg: snakeFgColor,
			Bg: snakeBgColor,
		},
	}
	e.list.PushBack(snake)
}
